package com.example.jdbi.config;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.util.Set;

/**
 * @author fanhang
 */
@AutoConfigureAfter(JdbiFactoryBean.class)
public class MapperBeanRegistrar extends ClassPathScanningCandidateComponentProvider implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata metadata, @NonNull BeanDefinitionRegistry registry) {
        MapperBeanDefinitionScanner scanner = new MapperBeanDefinitionScanner(registry);
        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents("com.example");
        for (BeanDefinition candidateComponent : beanDefinitions) {
            if (candidateComponent instanceof AnnotatedBeanDefinition beanDefinition) {
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(), "@JdbiMapper can only be specified on an interface");
                AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDefinition);
                //
                String className = beanDefinition.getBeanClassName();
                Class<?> clazz = ClassUtils.resolveClassName(className, null);
                this.registerMapperBean(registry, clazz);
            }
        }
    }

    private void registerMapperBean(BeanDefinitionRegistry registry, Class<?> mapperClass) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(JdbiMapperFactoryBean.class);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.setBeanClass(JdbiMapperFactoryBean.class);
        ConstructorArgumentValues argumentValues = definition.getConstructorArgumentValues();
        argumentValues.addGenericArgumentValue(mapperClass);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        registry.registerBeanDefinition(mapperClass.getSimpleName(), definition);
    }


    /**
     * 自定义扫描 MapperBean
     */
    public static class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
        public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            super(registry);
            super.addIncludeFilter(new AnnotationTypeFilter(JdbiMapper.class));
        }

        @Override
        protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
            return beanDefinition.getMetadata().isInterface();
        }
    }
}
